"""
Markdown parser for converting Markdown files to structured content.
"""

import re
from pathlib import Path
from typing import List, Optional

import markdown
from markdown.extensions import codehilite, fenced_code

from .content_types import (
    AnimatedContent,
    AnimationConfig,
    BulletListContent,
    CodeBlockContent,
    ContentNode,
    ContentType,
    DocumentStructure,
    ImageContent,
    VideoContent,
    Section,
    TextContent,
)


class MarkdownParser:
    """Parser for converting Markdown files to structured document representation."""

    def __init__(self, enable_animations: bool = True):
        self.md = markdown.Markdown(
            extensions=['fenced_code', 'codehilite'],
            extension_configs={
                'codehilite': {
                    'css_class': 'highlight'
                }
            }
        )
        self.enable_animations = enable_animations
        self.animation_parser = None
        
        if enable_animations:
            from ..animation.syntax.parser import AnimationParser
            self.animation_parser = AnimationParser()

    def parse_file(self, file_path: str) -> DocumentStructure:
        """Parse a Markdown file and return structured document."""
        path = Path(file_path)
        if not path.exists():
            raise FileNotFoundError(f"Markdown file not found: {file_path}")
        
        with open(path, 'r', encoding='utf-8') as f:
            content = f.read()
        
        return self.parse_content(content)

    def parse_content(self, content: str) -> DocumentStructure:
        """Parse Markdown content string and return structured document."""
        # Pre-process for animations if enabled
        animation_blocks = []
        if self.enable_animations and self.animation_parser:
            content, animation_blocks = self.animation_parser.parse_markdown_content(content)
        
        lines = content.split('\n')
        
        # Extract document title (first # heading or filename)
        title = self._extract_title(lines)
        
        # Parse sections
        sections = self._parse_sections(lines)
        
        # Post-process to integrate animations
        if animation_blocks:
            sections = self._integrate_animations(sections, animation_blocks)
        
        return DocumentStructure(title=title, sections=sections)

    def _extract_title(self, lines: List[str]) -> str:
        """Extract document title from the first # heading."""
        for line in lines:
            line = line.strip()
            if line.startswith('# '):
                return line[2:].strip()
        return "Untitled Presentation"

    def _parse_sections(self, lines: List[str]) -> List[Section]:
        """Parse lines into sections based on headings."""
        sections = []
        current_section = None
        current_content_lines = []
        
        i = 0
        while i < len(lines):
            line = lines[i].strip()
            
            # Check for heading
            if line.startswith('#'):
                # Save previous section if exists
                if current_section is not None:
                    content = self._parse_content_lines(current_content_lines)
                    current_section.content = content
                    sections.append(current_section)
                
                # Start new section
                level = self._get_heading_level(line)
                heading_text = self._extract_heading_text(line)
                current_section = Section(heading=heading_text, level=level, content=[])
                current_content_lines = []
            else:
                # Collect content lines
                current_content_lines.append(lines[i])
            
            i += 1
        
        # Don't forget the last section
        if current_section is not None:
            content = self._parse_content_lines(current_content_lines)
            current_section.content = content
            sections.append(current_section)
        
        return sections

    def _get_heading_level(self, line: str) -> int:
        """Get the level of a heading (number of # characters)."""
        level = 0
        for char in line:
            if char == '#':
                level += 1
            else:
                break
        return level

    def _extract_heading_text(self, line: str) -> str:
        """Extract the text from a heading line."""
        # Remove # characters and whitespace
        return re.sub(r'^#+\s*', '', line).strip()

    def _parse_content_lines(self, lines: List[str]) -> List[ContentNode]:
        """Parse content lines into ContentNode objects."""
        content_nodes = []
        i = 0
        
        while i < len(lines):
            line = lines[i].strip()
            
            if not line:  # Skip empty lines
                i += 1
                continue
            
            # Check for bullet list
            if line.startswith('-') or line.startswith('*'):
                bullet_items, lines_consumed = self._parse_bullet_list(lines[i:])
                if bullet_items:
                    content_nodes.append(ContentNode(
                        type=ContentType.BULLET_LIST,
                        data=BulletListContent(items=bullet_items)
                    ))
                i += lines_consumed
                continue
            
            # Check for image or video
            media_match = re.match(r'!\[([^\]]*)\]\(([^)]+)\)', line)
            if media_match:
                alt_text = media_match.group(1)
                media_path = media_match.group(2)
                
                # Check if it's a video file
                if self._is_video_file(media_path):
                    # Parse additional video metadata from alt_text
                    poster_frame = None
                    if '|' in alt_text:
                        parts = alt_text.split('|')
                        alt_text = parts[0].strip()
                        for part in parts[1:]:
                            if part.strip().startswith('poster='):
                                poster_frame = part.strip().split('=')[1]
                    
                    content_nodes.append(ContentNode(
                        type=ContentType.VIDEO,
                        data=VideoContent(
                            path=media_path, 
                            alt_text=alt_text,
                            poster_frame=poster_frame
                        )
                    ))
                else:
                    # Regular image
                    content_nodes.append(ContentNode(
                        type=ContentType.IMAGE,
                        data=ImageContent(path=media_path, alt_text=alt_text)
                    ))
                i += 1
                continue
            
            # Check for code block
            if line.startswith('```'):
                code_content, language, lines_consumed = self._parse_code_block(lines[i:])
                if code_content is not None:
                    content_nodes.append(ContentNode(
                        type=ContentType.CODE_BLOCK,
                        data=CodeBlockContent(code=code_content, language=language)
                    ))
                    i += lines_consumed
                    continue
            
            # Default to text content
            text_lines, lines_consumed = self._parse_text_paragraph(lines[i:])
            if text_lines:
                text_content = '\n'.join(text_lines).strip()
                if text_content:
                    content_nodes.append(ContentNode(
                        type=ContentType.TEXT,
                        data=TextContent(text=text_content)
                    ))
            i += lines_consumed
        
        return content_nodes

    def _parse_bullet_list(self, lines: List[str]) -> tuple[List[str], int]:
        """Parse consecutive bullet list items."""
        items = []
        i = 0
        
        while i < len(lines):
            line = lines[i].strip()
            if line.startswith('-') or line.startswith('*'):
                # Remove bullet marker and trim
                item_text = re.sub(r'^[-*]\s*', '', line).strip()
                if item_text:
                    items.append(item_text)
                i += 1
            elif line == '':
                # Skip empty lines within bullet list
                i += 1
            else:
                # End of bullet list
                break
        
        return items, i

    def _parse_code_block(self, lines: List[str]) -> tuple[Optional[str], Optional[str], int]:
        """Parse a fenced code block."""
        if not lines or not lines[0].strip().startswith('```'):
            return None, None, 0
        
        first_line = lines[0].strip()
        language = first_line[3:].strip() if len(first_line) > 3 else None
        
        code_lines = []
        i = 1
        
        while i < len(lines):
            line = lines[i].rstrip()
            if line.strip() == '```':
                # End of code block
                i += 1
                break
            code_lines.append(line)
            i += 1
        
        code_content = '\n'.join(code_lines) if code_lines else ''
        return code_content, language, i

    def _parse_text_paragraph(self, lines: List[str]) -> tuple[List[str], int]:
        """Parse consecutive text lines as a paragraph."""
        text_lines = []
        i = 0
        
        while i < len(lines):
            line = lines[i].strip()
            
            # Stop at empty line or special content
            if (not line or 
                line.startswith('#') or 
                line.startswith('-') or 
                line.startswith('*') or
                line.startswith('```') or
                re.match(r'!\[.*\]\(.*\)', line)):
                break
            
            text_lines.append(line)
            i += 1
        
        return text_lines, i if i > 0 else 1

    def _is_video_file(self, file_path: str) -> bool:
        """Check if the file path indicates a video file."""
        video_extensions = {'.mp4', '.avi', '.mov', '.wmv', '.flv', '.webm', '.mkv', '.m4v'}
        from pathlib import Path
        return Path(file_path).suffix.lower() in video_extensions
    
    def _integrate_animations(self, sections: List[Section], animation_blocks) -> List[Section]:
        """Integrate animation blocks with parsed sections."""
        if not self.animation_parser:
            return sections
        
        # Build a map of animation block markers to animation blocks
        animation_map = {}
        for i, block in enumerate(animation_blocks):
            animation_map[f"<!-- ANIMATION_BLOCK_{i} -->"] = block
            animation_map[f"<!-- /ANIMATION_BLOCK_{i} -->"] = None  # End marker
        
        updated_sections = []
        
        for section in sections:
            updated_content = []
            current_animation = None
            i = 0
            
            while i < len(section.content):
                content_node = section.content[i]
                should_add_node = True
                
                # Check if this content node contains animation markers
                if content_node.type == ContentType.TEXT:
                    text = content_node.data.text
                    
                    # Check for animation start marker
                    start_marker_found = None
                    for marker, block in animation_map.items():
                        if marker in text and block is not None:  # Start marker
                            current_animation = block
                            start_marker_found = marker
                            break
                    
                    if start_marker_found:
                        # Remove start marker and check if there's content left
                        clean_text = text.replace(start_marker_found, '').strip()
                        if clean_text:
                            content_node.data.text = clean_text
                        else:
                            should_add_node = False  # Skip empty marker nodes
                    
                    # Check for animation end marker
                    end_marker_found = None
                    for marker, block in animation_map.items():
                        if marker in text and block is None:  # End marker
                            end_marker_found = marker
                            break
                    
                    if end_marker_found:
                        # Remove end marker
                        clean_text = text.replace(end_marker_found, '').strip()
                        if clean_text:
                            content_node.data.text = clean_text
                        else:
                            should_add_node = False  # Skip empty marker nodes
                        current_animation = None
                
                # If we're in an animation block, create animated content
                if current_animation and should_add_node:
                    # Clean up any remaining animation markers in text content
                    if content_node.type == ContentType.TEXT:
                        text = content_node.data.text
                        for marker in animation_map.keys():
                            text = text.replace(marker, '')
                        content_node.data.text = text.strip()
                        
                        # Skip if text becomes empty after cleanup
                        if not content_node.data.text:
                            should_add_node = False
                    
                    if should_add_node:
                        animated_content = self.animation_parser.create_animated_content(
                            content_node, current_animation
                        )
                        updated_content.append(ContentNode(
                            type=ContentType.ANIMATED_CONTENT,
                            data=animated_content
                        ))
                elif should_add_node:
                    # Clean up any remaining animation markers even for non-animated content
                    if content_node.type == ContentType.TEXT:
                        text = content_node.data.text
                        for marker in animation_map.keys():
                            text = text.replace(marker, '')
                        text = text.strip()
                        if text:  # Only add if there's actual content
                            content_node.data.text = text
                            updated_content.append(content_node)
                    else:
                        updated_content.append(content_node)
                
                i += 1
            
            # Create updated section
            updated_section = Section(
                heading=section.heading,
                level=section.level,
                content=updated_content
            )
            updated_sections.append(updated_section)
        
        return updated_sections